/*
 * HashRecordSerializer_test.cpp
 *
 *  Created on: Oct 26, 2012
 *      Author: m
 */

#include "HashRecordSerializer.h"

#include <memory>
#include <iostream>
#include <string>
#include <fstream>
#include <cstring>
#include <cstdlib>
#include <sstream>

#define _MIN_ARGS 3
#define _MODE_ARG 1
#define _INFILE_ARG 2
#define _OUTFILE_ARG 3
#define _SERIALIZE_MODE "-s"
#define _DESERIALIZE_MODE "-d"
#define _BIN_BUFFER_SIZE 256 * 10000
#define _ATTR_DELIM ';'

typedef enum
{
    serialize, deserialize, invalid
} operation_mode_t;

void invalid_args()
{
    std::cerr << "Argumentos inválidos. " << std::endl << "Uso: hrs_ts ["
            << _SERIALIZE_MODE << "|" << _DESERIALIZE_MODE << "] input output"
            << std::endl;
    exit( 1 );
}

void invalid_mode()
{
    std::cerr << "Modo inválido. " << std::endl << "Uso: hrs_ts ["
            << _SERIALIZE_MODE << "|" << _DESERIALIZE_MODE << "] input output"
            << std::endl;
    exit( 2 );
}

operation_mode_t parse_mode( char* arg )
{
    if ( strcmp( arg, _SERIALIZE_MODE ) == 0 )
        return serialize;

    if ( strcmp( arg, _DESERIALIZE_MODE ) == 0 )
        return deserialize;

    return invalid;
}

void cant_open_files()
{
    std::cerr << "Error al abrir los archivos de entrada/salida. " << std::endl;
    exit( 3 );
}

int main( int argc, char* argv[] )
{
    std::fstream textFile;
    std::fstream binFile;
    std::shared_ptr<std::string> strBuffer;
    std::shared_ptr<HashRecord> hr_ptr;
    physical::byte binaryBuffer[_BIN_BUFFER_SIZE];
    HashRecordSerializer serializer;
    size_t serializationSize;
    physical::byte *bufferPtr;
    size_t inputFileSize;
    size_t totalDeserializedBytes;
    size_t bytesDeserialized;
    std::string phrase;
    std::string authorSurname;
    std::string authorName;
    size_t lineNumber;
    bool textFileFail;
    bool binFileFail;

    if ( argc < _MIN_ARGS + 1 )
        invalid_args();

    operation_mode_t mode = parse_mode( argv[_MODE_ARG] );

    switch ( mode )
    {
    case serialize:
        textFile.open( argv[_INFILE_ARG], std::ios_base::in );
        binFile.open( argv[_OUTFILE_ARG],
                std::ios_base::out | std::ios_base::trunc
                        | std::ios_base::binary );

        textFileFail = textFile.fail();
        binFileFail = binFile.fail();

        if ( textFileFail || binFileFail )
            cant_open_files();

        strBuffer.reset( new std::string );

        lineNumber = 0;
        while ( getline( textFile, *strBuffer ) )
        {
            std::stringstream lineBuffer( *strBuffer );
            getline( lineBuffer, phrase, _ATTR_DELIM );
            getline( lineBuffer, authorSurname, _ATTR_DELIM );
            if ( !getline( lineBuffer, authorName, _ATTR_DELIM ) )
                authorName = "";

            hr_ptr.reset(
                    new HashRecord( lineNumber, phrase, authorSurname,
                            authorName ) );

            serializationSize = serializer.serialize( hr_ptr, binaryBuffer );
            binFile.write( binaryBuffer, serializationSize );

            lineNumber++;
        }

        textFile.close();
        binFile.close();
        break;

    case deserialize:

        textFile.clear();
        binFile.clear();
        binFile.open( argv[_INFILE_ARG],
                std::ios_base::in | std::ios_base::binary );
        textFile.open( argv[_OUTFILE_ARG],
                std::ios_base::out | std::ios_base::trunc );

        textFileFail = textFile.fail();
        binFileFail = binFile.fail();

        if ( textFileFail || binFileFail )
            cant_open_files();

        bufferPtr = binaryBuffer;

        // Cargo el archivo completo en memoria
        binFile.read( binaryBuffer, _BIN_BUFFER_SIZE );
        inputFileSize = binFile.gcount();
        totalDeserializedBytes = 0;

        while ( totalDeserializedBytes < inputFileSize )
        {

            if ( totalDeserializedBytes > 0 )
                textFile << std::endl;

            bytesDeserialized = serializer.deserialize( bufferPtr, hr_ptr );

            totalDeserializedBytes += bytesDeserialized;
            bufferPtr += bytesDeserialized;

            textFile << hr_ptr->getFrase() << _ATTR_DELIM
                    << hr_ptr->getAutorApellido();

            if ( hr_ptr->getAutorNombre().size() > 0 )
                textFile << _ATTR_DELIM << hr_ptr->getAutorNombre();
        }

        textFile << std::endl;

        textFile.close();
        binFile.close();

        break;

    case invalid:
        invalid_mode();
        break;
    }

    return 0;
}

